//
// Created by QU on 2023/8/16.
//

#include <stdio.h>
#include <stdbool.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>

#include "paramPasser.h"
#include "utils.h"

// 判定参数是否设置的struct. 位于全局空间.
struct paramFlag paramFlag = {false, false, false, false, false, false, false, false, false};

void printHelpMessage(char **argv) {
    const char help_template[] = {
            "\nUsage: %s [OPTIONS]\n"
            "Example Param:  --vctype VC-12-3v --gentype 60A1DA8E --genframenum 30 --physicalid 32 --logicalid 30 --startmfi 10\n\n"
            "Options:\n"
            "  -S, --structuremode 0-512,9999  Structure mode                               \n"
            "  -v, --vctype=VC-'n1'-'n2'v        Set VC type.                               \n"
            "      example: 'VC-4-3v'  set VCtype to VC-4, set Split Channel to 3 channel.  \n"
            "  -g, --gentype=GEN_TYPE      Set generate Data type .                         \n"
            "      available GEN_TYPE:                                                      \n"
            "                 1. 60A1DA8E                                                   \n"
            "                 2. LOWERINC_HIGHERLOID                                        \n"
            "                 3. LOID_INC                                                   \n"
            "                 4. 00TOFF                                                     \n"
            "                 5. INPUTFILE                                                  \n"
            "      -i, --inputfile=FILEPATH   ONLY set -g,--gentype PATH works.             \n"
            "             generate Data will set stream to FILE stream.                     \n"
            "  -n, --genframenum=FRAMENUM  Set generate Data Frame number.                  \n"
            "  -P, --physicalid=PHYSICALID Set physical ID value.                           \n"
            "  -L, --logicalid=LOGICALID   Set logical ID value.                            \n"
            "  -m, --startmfi=START_MFI    Set starting point MFI value.                    \n"
            "  -V, --verbose               Verbose mode.  (May not work (QwQ) )             \n"
            "  -h, --help                  Show this help message and exit.                 \n\n"
            "Some Parameters sample like this:                                              \n"
            " Multiple Channel Generator:                                                   \n"
            "  --structuremode 0 --gentype 0 --genframenum 30                                \n"
            "  --structuremode 256 --gentype 2 --genframenum 30 --startphysicalid 0 --startlogicalid 0 --startmfi 100\n"
            "       You Can Also use short option:  -S 256 -g 0 -n 30 -P 10 -L 10 -m 50    Same than following:\n"
            "       --structuremode 1 --gentype 0 --genframenum 30 --startphysicalid 10 --startlogicalid 10 --startmfi 50\n"
            " Single Channel Generator:                                                   \n"
            "  --vctype VC-4-12v --gentype 0 --genframenum 10 --startphysicalid 0 --startlogicalid 0 --startmfi 100\n"

    };
    printf(help_template, argv[0]);
}


struct strParameter parameterPasserToStrParam(int argc, char *argv[]) {
    struct strParameter strParameterVal;

    static struct option long_options[] = {
            {"structuremode",   required_argument, 0, 'S'},
            {"vctype",          required_argument, 0, 'v'},
            {"gentype",         required_argument, 0, 'g'},
            {"inputfile",       required_argument, 0, 'i'},
            {"genframenum",     required_argument, 0, 'n'},
            {"startphysicalid", required_argument, 0, 'P'},
            {"startlogicalid",  required_argument, 0, 'L'},
            {"startmfi",        required_argument, 0, 'm'},
            {"help",            no_argument,       0, 'h'},
            {"verbose",         no_argument,       0, 'V'},
            {0,                 0,                 0, 0} // 用于指示长选项数组结束
    };

    int option_index = 0;
    // 使用getopt_long解析命令行参数
    int option;
    while ((option = getopt_long(argc, argv, "S:v:g:i:l:n:P:L:m:Vh", long_options, &option_index)) != -1) {
        switch (option) {
            case 'S':
                if (optarg) {
                    paramFlag.structuremode = true;
//                    strcpy_s(strParameterVal.structuremode, sizeof(strParameterVal.structuremode), optarg);
                    strncpy(strParameterVal.structuremode, optarg, sizeof(strParameterVal.structuremode));
                }
                break;

            case 'v':
                //                printf("option %s", long_options[option_index].name);
                if (optarg) {
                    //                    printf(" with arg %s", optarg);
                    paramFlag.vctype = true;
//                    strcpy_s(strParameterVal.vctype, sizeof(strParameterVal.vctype), optarg);
                    strncpy(strParameterVal.vctype, optarg, sizeof(strParameterVal.vctype));

                }
                break;
            case 'g':
                if (optarg) {
                    paramFlag.gentype = true;
//                    strcpy_s(strParameterVal.gentype, sizeof(strParameterVal.gentype), optarg);
                    strncpy(strParameterVal.gentype, optarg, sizeof(strParameterVal.gentype));

                }
                break;
            case 'i':
                if (optarg) {
                    paramFlag.inputfile = true;
//                    strcpy_s(strParameterVal.inputfile, sizeof(strParameterVal.inputfile), optarg);
                    strncpy(strParameterVal.inputfile, optarg, sizeof(strParameterVal.inputfile));

                }
                break;

            case 'n':
                if (optarg) {
                    paramFlag.genframenum = true;
//                    strcpy_s(strParameterVal.genframenum, sizeof(strParameterVal.genframenum), optarg);
                    strncpy(strParameterVal.genframenum, optarg, sizeof(strParameterVal.genframenum));

                }
                break;
            case 'P':
                if (optarg) {
                    paramFlag.physicalid = true;
//                    strcpy_s(strParameterVal.physicalid, sizeof(strParameterVal.physicalid), optarg);
                    strncpy(strParameterVal.physicalid, optarg, sizeof(strParameterVal.physicalid));

                }
                break;
            case 'L':
                if (optarg) {
                    paramFlag.logicalid = true;
//                    strcpy_s(strParameterVal.logicalid, sizeof(strParameterVal.logicalid), optarg);
                    strncpy(strParameterVal.logicalid, optarg, sizeof(strParameterVal.logicalid));
                }
                break;
            case 'm':
                if (optarg) {
                    paramFlag.startmfi = true;
//                    strcpy_s(strParameterVal.startmfi, sizeof(strParameterVal.startmfi), optarg);
                    strncpy(strParameterVal.startmfi, optarg, sizeof(strParameterVal.startmfi));

                }
                break;
            case 'V':
                paramFlag.verbose = true;
                break;
            case 'h':
                printHelpMessage(argv);
                exit(EXIT_SUCCESS);
                break;
            case '?':
                // 未知选项或缺少参数
                exit(EXIT_FAILURE);
            default:
                printf("Unknown option\n");
                //
                exit(0);
        }
    }
    return strParameterVal;
}


struct numParameter parseStrParameterToNumParameter(struct paramFlag flags, struct strParameter strParameter) {
    uint16_t defaultStructureMode = 9999;
    enum VC_TYPE defaultVCType = 0;
    uint16_t defaultSplitedCols = 0;
    uint16_t defaultPhysicalID = 0;
    uint16_t defaultLogicalID = 0;
    uint16_t defaultStartMFI = 0;
    enum GENERATE_DATA_TYPE defaultGenDataType = FIXED_60A1DA8E;
    size_t defaultGenFrameNum = 0;
    size_t defaultGenLength = 0; // 总 buffer 长度
    char defaultInputFilePath[256] = {0};

    if (flags.verbose == true) {
        VERBOSE_FLAG = true;
    }

    // structure mode & vcType
    if (flags.structuremode == false && flags.vctype == false) {
        printErrorMessageWithExtraMessage("structuremode or vcType is not set", "Aborting...");
        exit(EXIT_FAILURE);
    } else if (flags.structuremode == true && flags.vctype == true) {
        printWarningMessageWithExtraMessage("structuremode and vcType both setted.", "Ignore vcTypeset");
        defaultStructureMode = strtol(strParameter.structuremode, NULL, 10);
        defaultVCType = 0;
        defaultSplitedCols = 0;
    } else {
        if (flags.structuremode == true) {
            defaultStructureMode = strtol(strParameter.structuremode, NULL, 10);
        } else {
            // TODO: VCType valdating......
            uint16_t num1, num2;
            if (extractVCTypeValues(strParameter.vctype, &num1, &num2)) {
                if (VERBOSE_FLAG) {
                    printInfoMessage("VCType analyzing success.");
                }
            } else {
                printErrorMessage("VCType analyzing FAILED.");
                exit(EXIT_FAILURE);
            }
            defaultVCType = vcTypeConvertor(num1);
            defaultSplitedCols = num2;
        }
    }

    // genlength & genFrameNum
    if (flags.genframenum == false) {
        printErrorMessageWithExtraMessage("genframenum is not set", "Aborting...");
        exit(EXIT_FAILURE);
    } else {
        defaultGenFrameNum = strtol(strParameter.genframenum, NULL, 10);
        if (defaultGenFrameNum <= 0) {
            printErrorMessageWithExtraMessage("genframenum is not valid", "Aborting...");
            exit(EXIT_FAILURE);
        }
        // 1GB
        if (defaultGenFrameNum > 10000) {
            printWarningMessageWithExtraMessage("genframenum is too large", "Continue...");
            for (int i = 5; i > 0; i--) {
                printf("Continue? %d\n", i);
                sleep(1);
            }
        }
    }

    // gentype
    if (flags.gentype == false) {
        printWarningMessageWithExtraMessage("gentype is not set", "will set gentype to 60A1DA8E(type 0)");
        defaultGenDataType = FIXED_60A1DA8E;
    } else {
        // gentype setted
        if (strcmp(strParameter.gentype, "0") == 0) {
            defaultGenDataType = FIXED_60A1DA8E;
        } else if (strcmp(strParameter.gentype, "1") == 0) {
            defaultGenDataType = FIXED_LowerINC_HigherLOID;
        } else if (strcmp(strParameter.gentype, "2") == 0) {
            defaultGenDataType = FIXED_LogicINC;
        } else if (strcmp(strParameter.gentype, "3") == 0) {
            defaultGenDataType = FIXED_00TOFF;
        } else if (strcmp(strParameter.gentype, "INPUTFILE") == 0) {
            defaultGenDataType = INPUTFILE;
        } else {
            printWarningMessageWithExtraMessage("gentype is not valid", " will set gentype to 60A1DA8E(type 0)");
            defaultGenDataType = FIXED_60A1DA8E;
        }
    }

    // physicalID
    if (flags.physicalid == false) {
        printWarningMessageWithExtraMessage("physicalid is not set", "will set physicalID to 0");
        defaultPhysicalID = 0;
    } else {
        defaultPhysicalID = strtol(strParameter.physicalid, NULL, 10);
    }

    // logicalID
    if (flags.logicalid == false) {
        printWarningMessageWithExtraMessage("logicalid is not set", "will set logicalID to 0");
        defaultLogicalID = 0;
    } else {
        defaultLogicalID = strtol(strParameter.logicalid, NULL, 10);
    }

    // startMFI
    if (flags.startmfi == false) {
        printWarningMessageWithExtraMessage("startmfi is not set", "will set startmfi to 0");
        defaultStartMFI = 0;
    } else {
        defaultStartMFI = strtol(strParameter.startmfi, NULL, 10);
    }

    // inputfile
    if (flags.gentype == true && defaultGenDataType == INPUTFILE && flags.inputfile == false) {
        printErrorMessageWithExtraMessage("defaultGenDataType is INPUTFILE but filePath not set", "Aborting...");
        exit(EXIT_FAILURE);
    } else if (flags.gentype == true && defaultGenDataType == INPUTFILE && flags.inputfile == true) {
        // TODO: INPUTFILE mode implement.....
        printWarningMessageWithExtraMessage("INPUTFILE Not Supported (may not implement)", "INPUTFILE Will not work.");
        strncpy(defaultInputFilePath, strParameter.inputfile, sizeof(defaultInputFilePath));
//        exit(EXIT_FAILURE);
    }

    struct numParameter retVal = {0};
    retVal.numGenDataType = defaultGenDataType;
    retVal.numStructureMode = defaultStructureMode;

    retVal.numVCType = defaultVCType;
    retVal.numSplitedCols = defaultSplitedCols;

    retVal.numPhysicalID = defaultPhysicalID;
    retVal.numLogicalID = defaultLogicalID;
    retVal.numStartMFI = defaultStartMFI;
    retVal.numGenDataType = defaultGenDataType;
    retVal.numGenFrameNum = defaultGenFrameNum;

    strncpy(retVal.inputfilename, defaultInputFilePath, sizeof(retVal.inputfilename));

    return retVal;
}


static inline bool extractVCTypeValues(const char *inputString, uint16_t *n1, uint16_t *n2) {
    // 假设输入字符串的形式为 "VC-n1-n2v"
    const char *prefix = "VC-";

    if (strncmp(inputString, prefix, strlen(prefix)) != 0) {
        return false;
    }

    // 解析n1和n2
    const char *n1Start = inputString + strlen(prefix);
    char *n2Start;
    *n1 = strtol(n1Start, &n2Start, 10);
    if (n1Start == n2Start) {
        return false; // 无法解析n1
    }

    // 检查n2后面的字符是否合法
    if (*n2Start != '-' || *(n2Start + 1) == '\0') {
        return false;
    }

    if (!isdigit(*(n2Start + 1))) {
        return false;
    }

    *n2 = strtol(n2Start + 1, NULL, 10);
    return true;
}


void printNumParameter(const struct numParameter *params) {
    if (params->numStructureMode == 9999) { printf("numStructureMode: Single Channel Mode.\n"); }
    else { printf("numStructureMode: %hu\n", params->numStructureMode); }
    switch (params->numVCType) {
        case VC_4:
            printf("numVCType: VC_4\n");
            break;
        case VC_12:
            printf("numVCType: VC_12\n");
            break;
        default:
            if (paramFlag.structuremode == true) {
                printf("numVCType: %hu\n", params->numVCType);
            } else {
                printWarningMessage("numVCType Value Unknown");
            }
            break;
    }

    printf("numSplitedCols: %hu\n", params->numSplitedCols);
    printf("numPhysicalID: %hu\n", params->numPhysicalID);
    printf("numLogicalID: %hu\n", params->numLogicalID);
    printf("numStartMFI: %hu\n", params->numStartMFI);

    switch (params->numGenDataType) {
        case FIXED_60A1DA8E:
            printf("numGenDataType: FIXED_60A1DA8E (0)\n");
            break;
        case FIXED_LowerINC_HigherLOID:
            printf("numGenDataType: FIXED_LowerINC_HigherLOID (1)\n");
            break;
        case FIXED_LogicINC:
            printf("numGenDataType: FIXED_LogicINC (2)\n");
            break;
        case FIXED_00TOFF:
            printf("numGenDataType: FIXED_00TOFF (3)\n");
            break;

        case INPUTFILE:
            printf("numGenDataType: INPUTFILE (INPUTFILE)\n");
            break;

        default:
            printWarningMessage("numGenDataType: UNKNOWN VALUE");
            break;

    }
    printf("numGenFrameNum: %zu\n", params->numGenFrameNum);
    printf("inputfilename: %s\n", params->inputfilename);
}

#ifdef TEST_MODE
void testParamPasser(int argc, char *argv[]) {
    printDebugMessageWithExtraMessage(
            "------------------------------------------ Testing paramPasser ------------------------------------------",
            "");

    // set flags
    printDebugMessageWithExtraMessage("----\t test parameterPasserToStrParam \t----", "");
    struct strParameter strParam = parameterPasserToStrParam(argc, argv);
    printDebugMessageWithExtraMessage("----\t test parameterPasserToStrParam complete \t----", "");

    printDebugMessageWithExtraMessage("----\t test parseStrParameterToNumParameter \t----", "");
    struct numParameter numParam = parseStrParameterToNumParameter(paramFlag, strParam);
    printDebugMessageWithExtraMessage("----\t test parseStrParameterToNumParameter complete \t----", "");

    printInfoMessage("printNumParameter");
    printNumParameter(&numParam);


    printDebugMessageWithExtraMessage(
            "------------------------------------------ Testing paramPasser Complete ------------------------------------------",
            "\n");
}
#endif


